/*  This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2.1 of the License, or (at
    your option) any later version.
    For more details, see the GNU Lesser General Public License (www.fsf.org
    or the COPYING file somewhere in the package)
 */

#ifndef OSGI_PIMPL_HH
#define OSGI_PIMPL_HH

#include "OSGi.hh"

/*! @file OSGi/PImpl.hh
    @brief Class OSGi::PImpl header.
    @author @ref Guillaume_Terrissol
    @date 29th December 2007 - 26th May 2013
    @note This file is distributed under the LGPL license.
    Refer to the file COPYING (or http://www.fsf.org) for more information.
 */

#include <memory>

namespace OSGi
{
//------------------------------------------------------------------------------
//                            Private Implementation
//------------------------------------------------------------------------------

    /*! @brief Idiom "private implementation".
        @version 1.2

        @tparam TPrivate Type of p-impl (note: automatic)

        In order to give a class @b C a "private implementation (p-impl),
        simply :
        - use macro OSGI_PIMPL() in class @b C private section,
        - define the implementation (class @b C::Private { ... }) in @b source
        file,
        - initialize the @b pthis attribute (parameters will be forwarded to the
        matching @b C::Private constructor).

        @par Exemple
        @code
        class MyClass
        {
           ...
        private:

            OSGI_PIMPL()
        };

        class MyClass::Private
        {
        public:
            using Pointee = void*;
            Private(U32 pIndex);
            Pointee fee();
            ...
        };

        MyClass::MyClass(U32 pIndex)
            : pthis(pIndex)
        {
            ...
        }

        void MyClass::foo()
        {
            Private::Pointee    lPointer = pthis->fee();
            ...
        }
        @endcode
        @warning A class owning a p-impl must explicitely define its
        constructors, destructor, and private class in the same source file (so
        that the p-impl construction and destruction are possible).
     */
    template<class TPrivate>
    class PImpl
    {
    public:
        //! @name Constructor & destructor
        //@{
        template<class... TTs>
        explicit    PImpl(TTs&&... pArgs);  //!< Generic constructor.
                    ~PImpl();               //!< Destructor.
        //@}
        //! @name Operators
        //@{
        void        swap(PImpl& pPImpl);    //!< Permutation.
        TPrivate*   operator->() const;     //!< Arrow operator.
        TPrivate&   operator*() const;      //!< Dereference operator.


    private:

        std::unique_ptr<TPrivate>   mData;  //!< Held instance.
        //@}
    };


    /*! Allows to declare a p-impl in a class [private section].
     */
#define OSGI_PIMPL()                 \
        class Private;               \
        OSGi::PImpl<Private>    pthis;
}

#include "PImpl.inl"

#endif  // OSGI_PIMPL_HH
